/*************************************************************************
 *                     COPYRIGHT NOTICE
 *            Copyright 2024 Horizon Robotics, Inc.
 *                   All rights reserved.
 *************************************************************************/
#include <iostream>
#include <stdexcept>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <libdrm/drm_fourcc.h>
#include "gles_winsys.hpp"
#include "gles_shader.hpp"
#include "gles_debug.hpp"

#include "model_data.h"

class computeShaderApp {
public:
	explicit computeShaderApp(OptionParser &app_options,
				  const char* vs_shader,
				  const char* fs_shader,
				  const char* vs_screen_shader,
				  const char* fs_screen_shader);
	~computeShaderApp();

	void init(void);
	void run(void);
	void de_init(void);

private:
	enum {
		PATCH_NUM = 1,
		VERTICES_PER_PATCH = 4
	};
	void initWindow();
	void initGles();
	void draw();
	void cleanup();

	OptionParser &app_options;

	// Window sys info
	egl_winsys* winsys;
	// Shader info
	const char* vs_shader_name;
	const char* fs_shader_name;

	const char* vs_screen_shader_name;
	const char* fs_screen_shader_name;

	// GLES context resouurces
	GLuint texture_id;
	GLuint vao_id;
	GLuint vbo_id;
	GLuint ebo_id;
	GLuint quad_vao_id;
	GLuint quad_vbo_id;

	unsigned int framebuffer;
	unsigned int textureColorbuffer;


	uint32_t draw_program_id;
	uint32_t quad_program_id;
};

computeShaderApp::computeShaderApp(OptionParser &app_options,
				   const char* vs_shader, const char* fs_shader,
				   const char* vs_screen_shader, const char* fs_screen_shader)
	: app_options(app_options)
	, winsys(nullptr)
	, vs_shader_name(vs_shader)
	, fs_shader_name(fs_shader)
	, vs_screen_shader_name(vs_screen_shader)
	, fs_screen_shader_name(fs_screen_shader)
	, vao_id(0)
	, vbo_id(0)
	{}

computeShaderApp::~computeShaderApp() {
	if (winsys != nullptr) {
		winsys->de_init();
		delete winsys;
		winsys = nullptr;
	}
}

void computeShaderApp::init() {
	initWindow();	// egl
	initGles();		// open gl
}

void computeShaderApp::run() {
	draw();
}

void computeShaderApp::de_init() {
	this->~computeShaderApp();
}

void computeShaderApp::initWindow() {
	int32_t ret = 0;

	winsys = new egl_gbm_drm_init(app_options);

	ret = winsys->init();
	if (ret != egl_winsys::INIT_SUCCESS) {
		throw std::runtime_error("winsys init failed");
	}
}

void computeShaderApp::initGles() {
	int32_t ret = 0;

	//winsys->make_draw_current();

	glViewport(0,
		   0,
		   app_options.m_options[OptionParser::WIN_WIDTH].val,
		   app_options.m_options[OptionParser::WIN_HEIGHT].val);
	std::cout << "viewport w=" << app_options.m_options[OptionParser::WIN_WIDTH].val << " h=" << app_options.m_options[OptionParser::WIN_HEIGHT].val << std::endl;

	draw_program_id = create_program(vs_shader_name, fs_shader_name);
	//quad_program_id = create_program(vs_screen_shader_name, fs_screen_shader_name);
	std::cout << "main(): vs_path= " << vs_shader_name << std::endl;
	std::cout << "main(): fs_path= " << fs_shader_name << std::endl;
	std::cout << "main(): quad_vs_path= " << vs_screen_shader_name << std::endl;
	std::cout << "main(): quad_fs_path= " << fs_screen_shader_name << std::endl;
	

	// Triangle VAO 
	glGenVertexArrays(1, &vao_id);
	glGenBuffers(1, &vbo_id);
	glGenBuffers(1, &ebo_id);

	glBindVertexArray(vao_id);
	glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
	glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 9, g_vertices, GL_DYNAMIC_DRAW);

	//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_id);
	//glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * 6, g_indices, GL_STATIC_DRAW);


	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);	// position


	// print info
	const GLubyte* version = glGetString(GL_VERSION);
	printf("GL_VERSION: %s\n\n", version);

}

void computeShaderApp::draw() {


	while(1){
		glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);
		
		glUseProgram(draw_program_id);
		glBindVertexArray(vao_id);
		glDrawArrays(GL_TRIANGLES, 0, 3);
		glFinish();

		winsys->swap();
	}

	
	// bool wait_success = winsys->wait();
	// if(wait_success){
	// 	if (app_options.m_options[OptionParser::DUMP_IMAGE].val) {
			// winsys->dump_one_image();
	// 	} 
	// }else{
	// 	std::cout << "draw() wait sync failed" << std::endl;
	// }
	
	//std::this_thread::sleep_for(std::chrono::seconds(1));	// 等待前缓冲准备好

};

int main(int argc, char* argv[]) {
	OptionParser sample_options(argc, argv);
	const struct {
		std::string vertex_shader;
		std::string fragment_shader;
		std::string screen_vs_shader;
		std::string screen_fs_shader;
	} file_path_info = {
		.vertex_shader = sample_options.m_sampleBinpath + "shader.vs",
		.fragment_shader = sample_options.m_sampleBinpath + "shader.fs",
		.screen_vs_shader = sample_options.m_sampleBinpath + "screen_shader.vs",
		.screen_fs_shader = sample_options.m_sampleBinpath + "screen_shader.fs"
		
	};
	computeShaderApp app(sample_options,
			     file_path_info.vertex_shader.c_str(),
			     file_path_info.fragment_shader.c_str(),
				 file_path_info.screen_vs_shader.c_str(),
				 file_path_info.screen_fs_shader.c_str()
				 );

	try {
		app.init();		// gles init
		app.run();		// render

	} catch (const std::exception& e) {
		std::cerr << e.what() << std::endl;
		app.de_init();
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}